package cn.lcvc.xnc.authority.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.lcvc.xnc.authority.config.upload.FileUploadConfig;
import cn.lcvc.xnc.authority.dao.mapper.SysPermissionMapper;
import cn.lcvc.xnc.authority.dao.mapper.SysRoleMapper;
import cn.lcvc.xnc.authority.dao.mapper.SysUserMapper;
import cn.lcvc.xnc.authority.dao.mapper.SysUserRoleMapper;
import cn.lcvc.xnc.authority.model.SysRole;
import cn.lcvc.xnc.authority.model.SysUser;
import cn.lcvc.xnc.authority.model.dto.SysUserAuthority;
import cn.lcvc.xnc.authority.model.query.SysUserQuery;
import cn.lcvc.xnc.authority.service.SysUserService;import cn.lcvc.xnc.application.common.exception.model.MyServiceException;
import cn.lcvc.xnc.application.common.exception.model.MyWebException;
import cn.lcvc.xnc.application.common.utils.file.MyFileUpload;
import cn.lcvc.xnc.application.common.utils.password.SHA;
import cn.lcvc.xnc.application.common.utils.requests.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service
@Validated//表示开启sprint的校检框架，会自动扫描方法里的@Valid（@Valid注解一般写在接口即可）
@Slf4j
public class ISysUserService extends IBaseService<SysUser, SysUserQuery> implements SysUserService, UserDetailsService {
    @Autowired
    private SysRoleMapper sysRoleMapper;
    @Autowired
    private SysPermissionMapper sysPermissionMapper;
    @Autowired
    private SysUserMapper mapper;
    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;
    @Autowired
    private FileUploadConfig fileUploadConfig;

    /**
     * 是否是系统用户
     * 说明：系统用户不允许删除
     * @param user
     * @return true表示是超级管理员
     */
    private boolean isSystemUser(SysUser user){
        boolean result=false;
        if(user.getId()<=0){
            result=true;
        }
        return result;
    }


    /**
     * 获取图片的网址
     * @param fileName 文件名（不含路径）
     * @return 如果文件名为空，则返回文件名本身（也为空）
     */
    private String getImgWebUrl(String fileName){
        String imgWebUrl=null;
        if(StringUtils.hasLength(fileName)){
            imgWebUrl= RequestUtil.getBasePath() +fileUploadConfig.getCustomerProfilePictureBaseUrl()+fileName;
        }else{
            imgWebUrl=fileName;
        }
        return imgWebUrl;
        //组装成上传头像后的网址
        //return basePath+fileUploadConfig.getCustomerProfilePictureBaseUrl()+fileName;

    }

    /**
     * 在用户添加和编辑前的统一验证
     * @param sysUser
     */
    private void validateSaveOrUpdate(SysUser sysUser){
        //判断是否选择了部门
        if(sysUser.getDepartment()==null&&sysUser.getDepartment().getId()==null){
            throw new MyWebException("编辑失败：必须选择所属部门");
        }
        //判断是否选择了角色，要求必须选择一个角色。
        if(sysUser.getRoleIds()!=null&&sysUser.getRoleIds().length==0){
            throw new MyWebException("编辑失败：必须选择角色");
        }
    }


    /**
     *spring security接口UserDetailsService必须要实现的方法
     * @param username
     * @return
     * @throws UsernameNotFoundException 找不到该账户名的异常
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUserAuthority userAuthority=null;
        SysUser sysUser = ((SysUserMapper)mapper).findOneByUsername(username); //根据账户名查找指定账户信息
        if(sysUser !=null){
            if (sysUser.getStatus() == SysUser.SysUserStatus.LOCKED) {
                throw new LockedException("用户被锁定,请联系管理员");
            } else if (sysUser.getStatus() == SysUser.SysUserStatus.DISABLED) {
                throw new DisabledException("用户已作废");
            }
            //设置图片地址为完整的网址
            sysUser.setHeadImgUrl(this.getImgWebUrl(sysUser.getHeadImgUrl()));
            //将账户对象对象转换
            userAuthority = new SysUserAuthority();
            BeanUtils.copyProperties(sysUser, userAuthority);
            //为对象赋予权限
            //List<SysRole> roles= sysUserRoleMapper.findAllRoleByUserId(sysUser.getId());
            //if(roles.contains(new SysRole(-99))){//如果用户有超级管理员权限
            if(super.isSyperUser(sysUser.getId())){//如果是超级用户
                //获取权限表的所有权限
//                SysPermisssionQuery sysPermisssionQuery=new SysPermisssionQuery();
//                sysPermisssionQuery.setEnabled(true);//只获取有权限的
                userAuthority.setSysPermissions(sysPermissionMapper.findAll(null));
            }else{
                //否则按实际拥有的权限处理
                userAuthority.setSysPermissions(sysPermissionMapper.findByUserIdAndRoleEnabled(sysUser.getId(),true));
            }
        }else{
            throw new AuthenticationCredentialsNotFoundException("用户名不存在");
        }
        return userAuthority;
    }

    @Override
    public boolean login(String username, String password) {
        boolean result=false;
        int number= mapper.countByUsernameAndPassword(username, SHA.getResult(password));
        if(number==1){
            result=true;
        }else if(number>1){
            log.error("异常：管理员表中的"+username+"账户信息重复"+number+"次，请检查数据库");
        }
        return result;
    }

    @Override
    protected void beforeGet(Serializable id) {

    }

    @Override
    protected void beforeUpdate(SysUser sysUser) {
        if(sysUser.getId()==null){
            throw new MyWebException("编辑失败：id不能为空");
        }
        // 表单验证
        validateSaveOrUpdate(sysUser);
        //判断账户名是否重名
        if(mapper.countByUsernameAndIdNot(sysUser.getUsername(),sysUser.getId())>0){
            throw new MyWebException("编辑失败：账户名已存在");
        }
        //判断网名是否重名
        if(mapper.countByNickNameAndIdNot(sysUser.getNickName(),sysUser.getId())>0){
            throw new MyWebException("编辑失败：网名已存在");
        }
        /**
         * 重新处理账户和角色的关系。
         * 考虑到关系表没有自增id，且账户和角色关系的数量不会多，本来可以简化逻辑：先全部删除该账户的所有角色关系，再重新添加
         * 但为了保证角色和账户关系的建立时间可查，故采用规范的判断
         */
        //将前端传递过来的集合转换为set集合,顺便去重
        Set<Integer> roleIdsSet = new HashSet<Integer>(Arrays.asList(sysUser.getRoleIds()));
        //获取所有的角色
        List<SysRole> rolesOfAll=sysRoleMapper.findAll(null);
        //获取原来的角色
        List<SysRole> rolesOrigin=sysUserRoleMapper.findAllRoleByUserId(sysUser.getId());
        //处理账户和角色的关系
        for(SysRole role:rolesOfAll){
            if(roleIdsSet.contains(role.getId())){//如果前端勾选了该角色
                if(!rolesOrigin.contains(role)){//如果该角色原来不存在
                    //添加该角色关系
                    sysUserRoleMapper.save(sysUser.getId(),role.getId());
                }
            }else{//如果没有勾选该角色
                if(rolesOrigin.contains(role)) {//如果该角色原来存在
                    //移除该角色关系
                    sysUserRoleMapper.delete(sysUser.getId(),role.getId());
                }
            }
        }
    }

    @Override
    protected void beforeSave(SysUser sysUser) {
        // 表单验证
        validateSaveOrUpdate(sysUser);
        //判断账户名是否重名
        if(mapper.countByUsername(sysUser.getUsername())>0){
            throw new MyWebException("编辑失败：账户名已存在");
        }
        //判断网名是否重名
        if(mapper.countByNickName(sysUser.getNickName())>0){
            throw new MyWebException("编辑失败：网名已存在");
        }
        //设置初始密码为123456
        sysUser.setPassword(SHA.getResult("123456"));
    }

    @Override
    public void afterSave(SysUser sysUser) {
        //保存角色与账户的关系
        for(Integer roleId:sysUser.getRoleIds()){
            sysUserRoleMapper.save(sysUser.getId(),roleId);
        }
    }

    @Override
    public void deleteByUser(SysUser user, Long[] ids) {
        // 判断被删除的账户里有没有自己，避免出现自己删除自己的情况
        for(Long id:ids){
            if(id.equals(user.getId())){
                throw new MyServiceException("编辑失败：不允许删除自己的账户");
            }
        }
        //删除账户
        super.delete(ids);
    }

    @Override
    protected void beforeDelete(Serializable[] ids) {
        for(Serializable id:ids) {
            SysUser user=new SysUser(Long.valueOf(id.toString()));
            if(isSystemUser(user)){
                user=mapper.findById(id);
                throw new MyServiceException("删除失败："+user.getUsername()+"为系统固定账户，不允许删除");
            }
        }
        //删除管理账户的登陆日志

        //移除与角色的关系
        sysUserRoleMapper.deleteByUserIds(ids);
    }

    @Override
    protected void afterFindAll(List<SysUser> list) {
        for(SysUser user:list){
            //设置图片地址为完整的网址
            user.setHeadImgUrl(this.getImgWebUrl(user.getHeadImgUrl()));
            //设置关联属性
            //拥有的角色数量
            user.setRoleNumber(sysUserRoleMapper.countAllRoleByUserId(user.getId()));
            //拥有的权限数量
            user.setPermissionNumber(sysPermissionMapper.countByUserIdAndRoleEnabled(user.getId(),null));
        }
    }

    @Override
    public void updatePassword(String username, String password, String newPass, String confirmPass) {
        //在web层已对密码字段进行验证
        if(!newPass.equals(confirmPass)){
            throw new MyWebException("密码修改失败：确认密码与新密码必须相同");
        }
        if(this.login(username, password)){//说明原密码正确
            // 获取当前登录账户信息
            SysUser user=mapper.findOneByUsername(username);
            user.setPassword(SHA.getResult(newPass));
            mapper.update(user);
        }else{
            throw new MyServiceException("密码修改失败：原密码错误");
        }
    }

    @Override
    public String uploadHeadImgFile(SysUser user, MultipartFile file) {
        String fileNameOfUpload=null;//最终要上传的文件名
        if(file.isEmpty()){
            throw new MyWebException("请选择要上传的文件");//默认上传失败
        }else{// 如果验证无误，则执行上传
            //获取头像要保存的服务器物理路径
            String filePath=fileUploadConfig.getCustomerProfilePictureUploadBaseUrl();
            //创建存储目录:在第一次上传图片时生效
            FileUtil.mkdir(filePath);
            //获取文件名
            String fileName=file.getOriginalFilename();
            //获取文件扩展名
            String extensionName=FileUtil.extName(fileName);//获取文件扩展名
            if(StringUtils.hasLength(extensionName)){//如果有扩展名
                //将扩展名统一改为小写字母
                extensionName=extensionName.toLowerCase();
            }
            // 验证上传图片后缀名是否符合本站要求
            if(MyFileUpload.validateExtByDir(extensionName,null)){
                //保存文件：
                //根据账户id生成新的文件名
                fileName=user.getId()+"."+extensionName;//根据id重新生成文件名
                //获取原来账户头像的文件名
                String fileNameOrigin=user.getHeadImgUrl();
                //1.先上传文件
                try {
                    file.transferTo(new File(filePath+fileName));
                    //将头像信息存储到数据库中
                    user.setHeadImgUrl(fileName);
                    mapper.update(user);
                    //获取图片的网址
                    fileNameOfUpload=this.getImgWebUrl(fileName);
                } catch (IOException e) {
                    throw new MyWebException("上传失败："+e.getMessage());
                }
                //2.判断上传的文件名是否与原来的头像文件名一致（主要判断后缀名，如果上传的头像后缀名不同，则原头像不会被覆盖）
                if(StringUtils.hasLength(fileNameOrigin)){
                    if(!fileName.equals(fileNameOrigin)){//如果上传后的头像名与原来的不符
                        //则要移除原来的头像
                        FileUtil.del(filePath+fileNameOrigin);
                    }
                }
            }
        }
        return fileNameOfUpload;
    }
}
